package tetris;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class GameData {
	private BlockCell[][] boardData = new BlockCell[20][10];
	private ShapeStruct next;

	public ShapeStruct getNext() {
		return next;
	}

	public ShapeStruct getPreview() {
		return preview;
	}

	public ShapeStruct getCurrent() {
		return current;
	}

	private ShapeStruct preview;
	private ShapeStruct current;

	private int level = 1;
	private int removed = 0;
	private int score = 0;
	private int levelScore = 0;
	private int ticks = 1000;
	private Runnable onRemove;
	private ExecutorService thread = Executors.newSingleThreadExecutor();

	public void setOnRemove(Runnable onRemove) {
		this.onRemove = onRemove;
	}

	public GameData() {

		for (int i = 0; i < 20; i++) {
			for (int j = 0; j < 10; j++) {
				boardData[i][j] = new BlockCell();
			}
		}
		randomShape();
	}

	public void randomShape() {
		var baseShapes = Block.getBlocks();
		var ran = Math.random();
		int index = (int) (ran * baseShapes.length);
		var b = baseShapes[index];
		ShapeStruct shape = new ShapeStruct(b);
		if (current == null) {
			current = shape;
			randomShape();
		} else {
			next = shape;
		}

		var d = (int) (Math.random() * 3);
		for (int r = 0; r < d; r++) {
			next.changeDirection();
		}
	}

	public void changeDirection() {
		current.changeDirection();
	}

	public boolean isFull() {
		if (current.getTop() == 0) {
			for (var row = 0; row < current.getHeight(); row++) {
				for (var col = 0; col < current.getWidth(); col++) {
					var cell = boardData[current.getTop() + row][current.getLeft() + col];
					if (this.current.getCell(row, col) && cell.visable)
						return true;
				}
			}
		}
		return false;
	}

	public synchronized void moveDown() {

		if (!this.isBottom()) {
			current.moveDown();
		} else {

			remove();

		}
	}

	private void removeRows() {
		if (preview != null) {
			for (var row = 0; row < preview.getHeight(); row++) {
				for (var col = 0; col < preview.getWidth(); col++) {
					if (preview.getCell(row, col)) {
						var cell = boardData[preview.getTop() + row][preview.getLeft() + col];
						cell.visable = true;
						cell.color = preview.color;
					}
				}
			}
		}
		preview = null;

		int removeCount = 0;
		for (int row = 0; row < 20; row++) {
			boolean b = true;
			for (int col = 0; col < 10; col++) {
				var cube = boardData[row][col].visable;
				b = b && cube;
			}

			if (b) {
				removed++;
				removeCount++;
				for (int ui = row - 1; ui >= 0; ui--) {
					for (int uj = 0; uj < 10; uj++) {
						var pre = boardData[ui][uj];
						boardData[ui + 1][uj].visable = pre.visable;
						boardData[ui + 1][uj].color = pre.color;
					}
				}
			}
		}

		var tempScore = 0;
		if (removeCount == 1)
			tempScore += 100;
		if (removeCount == 2)
			tempScore += 200;
		if (removeCount == 3)
			tempScore += 400;
		if (removeCount == 4)
			tempScore += 400;

		if (removeCount > 0) {
			if (onRemove != null) {
				thread.execute(onRemove);
			}
		}
		
		score += tempScore;
		levelScore += tempScore;

		if (levelScore >= 2000) {
			if (level < 12) {
				level++;
				ticks = 1065 - level * 65;
			} else
				level = 1;

			levelScore = 0;
		}
	}

	public boolean isBlock() {

		for (var row = 0; row < current.getHeight(); row++) {
			for (var col = 0; col < current.getWidth(); col++) {

				if (current.getLeft() > 0 && current.getCell(row, col)
						&& boardData[current.getTop() + row][current.getLeft() + col - 1].visable)
					return true;
				if ((current.getLeft() + current.getWidth()) < 10 && current.getCell(row, col)
						&& boardData[current.getTop() + row][current.getLeft() + col + 1].visable)
					return true;
			}
		}
		return false;
	}

	public void moveLeft() {
		if (!isBlock())
			current.moveLeft();
	}

	public void moveRight() {
		if (!isBlock())
			current.moveRight();
	}

	public synchronized void drop() {
		int srow = current.getHeight();
		int scol = current.getWidth();

		int ntop = 20 - srow;
		int curtop = current.getTop();
		for (int _top = curtop; _top < ntop; _top++) {
			for (int row = 0; row < srow; row++) {
				for (int col = 0; col < scol; col++) {
					var boardIndex = _top + row + 1;
					var cubeBaseD = boardData[boardIndex][current.getLeft() + col];
					var scube = current.getCell(row, col);
					if (cubeBaseD.visable && scube) {
						current.moveDown(_top - curtop);
						remove();
						return;
					}
				}
			}

		}
		current.moveDown(ntop - curtop);

		remove();
	}

	private void remove() {
		preview = current;
		current = next;
		removeRows();
		randomShape();
	}

	public synchronized void moveQuickly() {
		if (!this.isBottom()) {
			current.moveDown();
		} else {
			remove();
		}
	}

	boolean isBottom() {

		if (current.getTop() + current.getHeight() < 20) {
			for (var row = 0; row < current.getHeight(); row++) {
				for (var col = 0; col < current.getWidth(); col++) {
					int br = current.getTop() + row + 1;
					int bc = current.getLeft() + col;
					boolean b = boardData[br][bc].visable;

					if (current.getCell(row, col))
						if (b)
							return true;
				}
			}
		}
		return current.getTop() + current.getHeight() == 20;
	}

	public BlockCell[][] getBoardData() {
		return boardData;
	}

	public int getLevel() {
		return level;
	}

	public int getRemoved() {
		return removed;
	}

	public int getScore() {
		return score;
	}

	public int getTicks() {
		return ticks;
	}

}
